import json
import random
from datetime import datetime, timedelta
from typing import Awaitable

import redis
from flask import Blueprint, redirect, jsonify, request
from flask_jwt_extended import get_jwt_identity, verify_jwt_in_request
from sqlalchemy import func, and_
from werkzeug import Response
from models import Word, Sentence, Meaning, Collocation, StudyLog, db, get_redis_connection, Relative, Looklike
from utils.algorithm import get_next_review_date

word_api = Blueprint('word_api', __name__)


@word_api.before_request
def before_request():
    verify_jwt_in_request()


@word_api.route('/')
def index():
    return 'word'


def getWordBaseDetailById(_id: int) -> dict:
    word = Word.query.get(_id)
    sentences = Sentence.query.filter_by(word_id=word.id).all()

    s_list = [{
        'content': s.content,
        'from_word_id': word.id
    } for s in sentences]
    meanings = Meaning.query.filter_by(word_id=word.id).all()

    m_list = [{
        'part_of_speech': m.part_of_speech,
        'definition': m.definition,
        'from_word_id': word.id
    } for m in meanings]
    collocations = Collocation.query.filter_by(word_id=word.id).all()

    c_list = [{
        'content': c.content,
        'from_word_id': word.id
    } for c in collocations]
    relatives = Relative.query.filter_by(word_id=word.id).all()

    r_list = [{
        'content': r.content,
        'from_word_id': word.id
    } for r in relatives]

    word_detail = {
        'id': word.id,
        'word': word.word,
        'language': word.language,
        'sentences': s_list,
        'root_word': word.root_word,
        'root_meaning': word.root_meaning,
        'meanings': m_list,
        'collocations': c_list,
        'relative_words': r_list
    }

    return word_detail


def getWordDetailById(_id: int) -> dict:
    BaseDetail = getWordBaseDetailById(_id)
    right_option = random.choice(BaseDetail['meanings'])
    options = Meaning.query.filter(Meaning.word_id != _id).order_by(func.random()).limit(3).all()
    BaseDetail['right_option'] = {
        'part_of_speech': right_option["part_of_speech"],
        'definition': right_option["definition"]
    }
    BaseDetail['options'] = [
        {
            'part_of_speech': _.part_of_speech,
            'definition': _.definition
        } for _ in options
    ]
    BaseDetail['similar_options'] = [_.word for _ in Looklike.query.filter_by(word_id=_id).all()]

    return BaseDetail


@word_api.route('/study/<wid>', methods=['GET'])
def wordStudyDetail(wid: int) -> dict:
    current_user_id = get_jwt_identity()
    redis_client = get_redis_connection()
    if redis_client.exists(f'study:{current_user_id}:{wid}'):
        data = redis_client.hgetall(f'study:{current_user_id}:{wid}')
        data_ = {k: json.loads(v) for k, v in data.items()}
        return data_
    data = getWordDetailById(wid)
    mapping_ = {k: json.dumps(v) for k, v in data.items()}
    redis_client.hset(f'study:{current_user_id}:{wid}', mapping=mapping_)
    return data


@word_api.route('/review/<wid>', methods=['GET'])
def wordReviewDetail(wid: int) -> dict:
    current_user_id = get_jwt_identity()
    redis_client = get_redis_connection()
    if redis_client.exists(f'review:{current_user_id}:{wid}'):
        data = redis_client.hgetall(f'study:{current_user_id}:{wid}')
        data_ = {k: json.loads(v) for k, v in data.items()}
        return data_
    data = getWordDetailById(wid)
    mapping_ = {k: json.dumps(v) for k, v in data.items()}
    redis_client.hset(f'study:{current_user_id}:{wid}', mapping=mapping_)
    return data


@word_api.route('/study/yes/<wid>', methods=['POST'])
def studyWordYes(wid: int):
    """
    @param wid: 当前单词的id
    @return: 返回下一个单词的数据
    """
    current_user_id = get_jwt_identity()
    redis_client = get_redis_connection()
    data = request.get_json()
    flag = data.get('flag')
    if (redis_client.exists(f'study:{current_user_id}') and
            redis_client.exists(f'study:{current_user_id}:{wid}')):
        assert isinstance(flag, int)
        study_times = redis_client.hget(f'study:{current_user_id}:{wid}', 'times')
        if int(study_times) >= 2:
            wid = redis_client.lpop(f'study:{current_user_id}')
            # 这里需要在数据库中更新用户的学习记录，并且通过算法生成该用户对该单词学习的下一时间
            next_date = get_next_review_date(datetime.utcnow(), 0)
            new_log = StudyLog(user_id=current_user_id, word_id=wid, next_study_time=next_date)
            db.session.add(new_log)
            db.session.commit()

        # 在原来基础上+1
        redis_client.hincrby(f'study:{current_user_id}:{wid}', 'times', 1)
        # 从左边pop出来，然后push到右边的队尾
        item_id = redis_client.lpop(f'study:{current_user_id}')
        # 获取List的长度
        list_length = redis_client.llen(f'study:{current_user_id}')
        if list_length > 1:
            # 生成一个随机索引
            random_index = random.randint(0, list_length - 1)
            # 获取随机索引位置的元素
            pivot = redis_client.lindex(f'study:{current_user_id}', random_index)
            # 向List的随机位置插入数据
            redis_client.linsert(f'study:{current_user_id}', 'before', pivot, item_id)
        else:
            redis_client.rpush(f'study:{current_user_id}', item_id)

        if flag:
            the_first_one_id = redis_client.lindex(f'study:{current_user_id}', 0)
            the_first_one = redis_client.hgetall(f'study:{current_user_id}:{the_first_one_id}')
            data_ = {k: json.loads(v) for k, v in the_first_one.items()}
            data_['left_study'] = redis_client.llen(f'study:{current_user_id}')
            return data_
        return {'success': True}
    return {'error': False, 'data': 'something went wrong'}


# 点击下一词
@word_api.route('/study/btn_next/<wid>', methods=['POST', "GET"])
def btn_next(wid):
    current_user_id = get_jwt_identity()
    redis_client = get_redis_connection()
    if (redis_client.exists(f'study:{current_user_id}') and
            redis_client.exists(f'study:{current_user_id}:{wid}')):
        study_times = redis_client.hget(f'study:{current_user_id}:{wid}', 'times')
        if int(study_times) >= 2:
            wid = redis_client.lpop(f'study:{current_user_id}')
            # 这里需要在数据库中更新用户的学习记录，并且通过算法生成该用户对该单词学习的下一时间
            next_date = get_next_review_date(datetime.utcnow(), 0)
            new_log = StudyLog(user_id=current_user_id, word_id=wid, next_study_time=next_date)
            db.session.add(new_log)
            db.session.commit()
        the_first_one_id = redis_client.lindex(f'study:{current_user_id}', 0)
        the_first_one = redis_client.hgetall(f'study:{current_user_id}:{the_first_one_id}')
        data_ = {k: json.loads(v) for k, v in the_first_one.items()}
        data_['left_study'] = redis_client.llen(f'study:{current_user_id}')
        return data_
    return {'error': False, 'data': 'something went wrong'}


# 点击记错了
@word_api.route('/study/btn_error/<wid>', methods=['POST', "GET"])
def btn_error(wid):
    current_user_id = get_jwt_identity()
    redis_client = get_redis_connection()
    if redis_client.exists(f'study:{current_user_id}:{wid}'):
        redis_client.hset(f'study:{current_user_id}:{wid}', 'times', 0)
        # 从左边pop出来，然后push到右边的队尾
        item_id = redis_client.lpop(f'study:{current_user_id}')
        # 获取List的长度
        list_length = redis_client.llen(f'study:{current_user_id}')
        if list_length > 1:
            # 生成一个随机索引
            random_index = random.randint(0, list_length - 1)
            # 获取随机索引位置的元素
            pivot = redis_client.lindex(f'study:{current_user_id}', random_index)
            # 向List的随机位置插入数据
            redis_client.linsert(f'study:{current_user_id}', 'before', pivot, item_id)
        else:
            redis_client.rpush(f'study:{current_user_id}', item_id)
        the_first_one_id = redis_client.lindex(f'study:{current_user_id}', 0)
        the_first_one = redis_client.hgetall(f'study:{current_user_id}:{the_first_one_id}')
        data_ = {k: json.loads(v) for k, v in the_first_one.items()}
        data_['left_study'] = redis_client.llen(f'study:{current_user_id}')
        return data_
    return {'error': False, 'data': 'something went wrong'}


@word_api.route('/study/no/<wid>', methods=['POST'])
def studyWordNo(wid: int):
    """
    @param wid: 当前单词的id
    @return: 返回下一个单词的数据
    """
    current_user_id = get_jwt_identity()
    redis_client = get_redis_connection()
    if redis_client.exists(f'study:{current_user_id}:{wid}'):
        redis_client.hset(f'study:{current_user_id}:{wid}', 'times', 0)
        # 获取List的长度
        list_length = redis_client.llen(f'study:{current_user_id}')
        if list_length > 1:
            # 从左边pop出来，然后push到右边的队尾
            item_id = redis_client.lpop(f'study:{current_user_id}')
            # 生成一个随机索引
            random_index = random.randint(0, list_length - 1)
            # 获取随机索引位置的元素
            pivot = redis_client.lindex(f'study:{current_user_id}', random_index)
            # 向List的随机位置插入数据
            redis_client.linsert(f'study:{current_user_id}', 'before', pivot, item_id)
        return {'success': True}
    return {'error': False, 'data': 'something went wrong'}


@word_api.route('/review/yes/<wid>', methods=['POST'])
def reviewWordYes(wid: int):
    """
    @param wid: 当前单词的id
    @return: 返回下一个单词的数据
    """
    current_user_id = get_jwt_identity()
    redis_client = get_redis_connection()
    if redis_client.exists(f'review:{current_user_id}:{wid}'):
        review_times = redis_client.hget(f'review:{current_user_id}:{wid}', 'times')
        if int(review_times) >= 2:
            wid = redis_client.lpop(f'review:{current_user_id}')
            log = StudyLog.query.filter_by(user_id=current_user_id, word_id=wid).first()
            if log:
                next_date = get_next_review_date(datetime.utcnow(), log.study_times)
                new_log = StudyLog(user_id=current_user_id, word_id=wid, next_study_time=next_date,
                                   study_times=log.study_times + 1)
                db.session.add(new_log)
                db.session.commit()
            else:
                return {'error': '未知错误'}
        else:
            # 在原来基础上+1
            redis_client.hincrby(f'review:{current_user_id}:{wid}', 'times', 1)
            # 获取List的长度
            list_length = redis_client.llen(f'review:{current_user_id}')
            if list_length > 1:
                # 从左边pop出来，然后push到右边的队尾
                item_id = redis_client.lpop(f'review:{current_user_id}')
                # 生成一个随机索引
                random_index = random.randint(0, list_length - 1)
                # 获取随机索引位置的元素
                pivot = redis_client.lindex(f'review:{current_user_id}', random_index)
                # 向List的随机位置插入数据
                redis_client.linsert(f'review:{current_user_id}', 'before', pivot, item_id)
        the_first_one_id = redis_client.lindex(f'review:{current_user_id}', 0)
        the_first_one = redis_client.hgetall(f'review:{current_user_id}:{the_first_one_id}')
        data_ = {k: json.loads(v) for k, v in the_first_one.items()}
        data_['left_review'] = redis_client.llen(f'review:{current_user_id}')
        return data_
    return redirect('/api/user/goreview')


@word_api.route('/review/no/<wid>', methods=['POST'])
def reviewWordNo(wid: int):
    """
    @param wid: 当前单词的id
    @return: 返回下一个单词的数据
    """
    current_user_id = get_jwt_identity()
    redis_client = get_redis_connection()
    if redis_client.exists(f'review:{current_user_id}:{wid}'):
        redis_client.hset(f'review:{current_user_id}:{wid}', 'times', 0)
        # 获取List的长度
        list_length = redis_client.llen(f'review:{current_user_id}')
        if list_length > 1:
            # 从左边pop出来，然后push到右边的队尾
            item_id = redis_client.lpop(f'review:{current_user_id}')
            # 生成一个随机索引
            random_index = random.randint(0, list_length - 1)
            # 获取随机索引位置的元素
            pivot = redis_client.lindex(f'review:{current_user_id}', random_index)
            # 向List的随机位置插入数据
            redis_client.linsert(f'review:{current_user_id}', 'before', pivot, item_id)
        the_first_one_id = redis_client.lindex(f'review:{current_user_id}', 0)
        the_first_one = redis_client.hgetall(f'review:{current_user_id}:{the_first_one_id}')
        data_ = {k: json.loads(v) for k, v in the_first_one.items()}
        data_['left_review'] = redis_client.llen(f'review:{current_user_id}')
        return data_
    return redirect('/api/user/goreview')


@word_api.route('/wordList/<bookid>', methods=['POST'])
def wordList(bookid):
    bookid = 1
    wordData = Word.query.all()
    wList = []
    for word in wordData:
        meanings = Meaning.query.filter_by(word_id=word.id).all()
        m_list = [{
            'part_of_speech': m.part_of_speech,
            'definition': m.definition
        } for m in meanings]
        wList.append({
            'wid': word.id,
            'word': word.word,
            'meanings': m_list,
            'num': len(m_list)
        })
    return wList
